package com.carful.xigua.modules.common.config;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.annotation.JSONField;

import com.carful.xigua.modules.common.entity.RestBody;
import com.carful.xigua.modules.common.filter.JwtFilter;
import com.carful.xigua.modules.user.entity.User;
import com.carful.xigua.modules.user.service.impl.UserServiceImpl;
import jakarta.servlet.http.HttpServletResponse;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.Objects;

/**
 * @author Administrator
 * Date 2024/3/29 13:48
 * Description
 */

@Configuration
@Slf4j
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig {

    @Autowired
    private UserServiceImpl userService;

    @Autowired
    private JwtFilter jwtFilter;

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class LoginUserDetails implements UserDetails {
        private User user;

        @JSONField(serialize = false)
        private List<? extends GrantedAuthority> authorities;

        @Override
        public Collection<? extends GrantedAuthority> getAuthorities() {
            return authorities;
        }

        @Override
        public String getPassword() {
            return this.getUser().getPassword();
        }

        @Override
        public String getUsername() {
            return this.getUser().getUsername();
        }

        @Override
        public boolean isAccountNonExpired() {
            return true;
        }

        @Override
        public boolean isAccountNonLocked() {
            return true;
        }

        @Override
        public boolean isCredentialsNonExpired() {
            return true;
        }

        @Override
        public boolean isEnabled() {
            return true;
        }
    }

    @Bean("PasswordEncoder")
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public SecurityContextLogoutHandler securityContextLogoutHandler() {
        return new SecurityContextLogoutHandler();
    }

    @Bean
    public UserDetailsService userDetailsService() {
        return username -> {
            // 查询数据库用户信息
            User user = userService.selectOne(username);
            if (Objects.isNull(user)) {
                throw new RuntimeException("no username");
            }
            return new LoginUserDetails(user, user.getAuthorities());
        };
    }


    @Bean
    public AuthenticationManager authenticationManager(
            UserDetailsService userDetailsService,
            PasswordEncoder passwordEncoder) {
        DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
        authenticationProvider.setUserDetailsService(userDetailsService);
        authenticationProvider.setPasswordEncoder(passwordEncoder);

        return new ProviderManager(authenticationProvider);
    }

    public static void response(HttpServletResponse response, String res) throws IOException {
        response.setStatus(200);
        response.setContentType("application/json");
        response.setCharacterEncoding("UTF-8");
        response.getWriter().print(res);
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http,
                                           AuthenticationManager authenticationManager) throws Exception {


        http
            .authorizeHttpRequests(
                a -> {
                    a.requestMatchers("/static/**",
                                    "/user/login/**",
                                    "/index/**")
                            .permitAll()
                            .anyRequest().authenticated();
                }
            )
            .logout(a -> a.disable())
            .csrf(a -> a.disable())
            .authenticationManager(authenticationManager);

        // 添加处理器
        http.exceptionHandling(a -> a.authenticationEntryPoint(
                // 认证失败
                (request, response, e) -> {
                    log.error("认证失败: {}", e.getMessage());
                    response(response, JSON.toJSONString(RestBody.build(401,null, "认证失败", null)));
                }
        ).accessDeniedHandler(
                // 授权失败
                (request, response, e) -> {
                    log.error("授权失败: {}", e.getMessage());
                    response(response, JSON.toJSONString(RestBody.build(400, null, "授权失败", null)));
                }
        ));

        // jwt 过滤器
        http.addFilterBefore(jwtFilter, LogoutFilter.class);

        // 跨域
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.addAllowedOrigin("*");
        corsConfiguration.addAllowedHeader("*");
        corsConfiguration.addAllowedMethod("*");
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", corsConfiguration);
        http.cors(a -> a.configurationSource(source));

        return http.build();
    }


//    @Bean
//    public WebSecurityCustomizer webSecurityCustomizer() {
//        return (web) -> web.ignoring().anyRequest("/ignore1", "/ignore2");
//    }
}
